
#include <iostream>
#include <ctime>
#include <string>
#include <vector>
#include <algorithm>
#include <stdlib.h>

#define GL_GLEXT_PROTOTYPES
#define GLFW_INCLUDE_GL3
#include <GL/glew.h>
#include <GL/glfw.h>


using namespace std;

const float vertexPositions[] = {
	0.0f,    0.5f, 0.0f, 1.0f,
	0.5f, -0.366f, 0.0f, 1.0f,
	-0.5f, -0.366f, 0.0f, 1.0f,
	1.0f,    0.0f, 0.0f, 1.0f,
	0.0f,    1.0f, 0.0f, 1.0f,
	0.0f,    0.0f, 1.0f, 1.0f
};

const float vertexPositions2[] = {
	0.75f, 0.75f, 0.0f, 1.0f,
	0.45f, 0.45f, 0.0f, 1.0f,
	0.75f, 0.55f, 0.0f, 1.0f
};

const std::string strVertexShader(
	"#version 330\n"
	"layout (location = 0) in vec4 position;\n"
	"layout (location = 1) in vec4 color;\n"

	"smooth out vec4 theColor;\n"

	"void main()\n"
	"{\n"
    "gl_Position = position;\n"
    "theColor = color;\n"
	"}\n"
);

const std::string strFragmentShader(
	"#version 330\n"

	"smooth in vec4 theColor;\n"

	"out vec4 outputColor;\n"

	"void main()\n"
	"{\n"
	"outputColor = theColor;\n"
	"}\n"
);

const std::string strFragmentShader2(
	"#version 330\n"
	"out vec4 outputColor;\n"
	"void main()\n"
	"{\n"
	"   outputColor = vec4(0.0f, 0.0f, 1.0f, 0.0f);\n"
	"}\n"
);



GLuint vertexBuffer[2];

GLuint renderProgram;
GLuint renderProgram2;

string getCurrentTime() {
	time_t t = time(0);
	struct tm *now = localtime(&t);

	string result = "";
	char n[4];
	itoa(now->tm_year + 1900, n, 10);
	result += n;
	result += "-";
	itoa(now->tm_mon + 1, n, 10);
	result += ((now->tm_mon + 1) < 10) ? "0" : "";
	result += n;
	result += "-";
	itoa(now->tm_mday, n, 10);
	result += ((now->tm_mday + 1) < 10) ? "0" : "";
	result += n;
	result += " ";
	itoa(now->tm_hour, n, 10);
	result += ((now->tm_hour + 1) < 10) ? "0" : "";
	result += n;
	result += ":";
	itoa(now->tm_min, n, 10);
	result += ((now->tm_sec + 1) < 10) ? "0" : "";
	result += n;
	result += ":";
	itoa(now->tm_sec, n, 10);
	result += n;

	return result;
}


GLuint createShader(GLenum type, const string shaderCode) {
	GLuint shader = glCreateShader(type);
	const char* strData = shaderCode.c_str();
	glShaderSource(shader, 1, &strData, NULL);

	glCompileShader(shader);

	GLint status;

	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (status == GL_FALSE) {
		GLint logLength;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
		GLcharARB *logMessage = new GLcharARB[logLength + 1];
		glGetShaderInfoLog(shader, logLength, NULL, logMessage);

		const char *strShaderType = NULL;
		switch(type) {
			case GL_VERTEX_SHADER: strShaderType = "vertex"; break;
			case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break;
			case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break;
		}

		cout << getCurrentTime() << ": " << strShaderType << " shader compilation error: " << endl << "\t" << logMessage << endl;

		delete[] logMessage;

	}

	return shader;
}

GLuint createProgram(const vector<GLuint> shaders) {

	GLuint program = glCreateProgram();
	for (unsigned int i = 0; i < shaders.size(); i++) {
		glAttachShader(program, shaders[i]);
	}

	glLinkProgram(program);

	GLint status;
	glGetProgramiv(program, GL_LINK_STATUS, &status);
	if (status == GL_FALSE) {
		GLint logLength;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);

		GLcharARB *message = new GLcharARB[logLength + 1];
		glGetProgramInfoLog(program, logLength, NULL, message);

		cout << getCurrentTime() << ": " << "shader link error: " << endl << "\t" << message << endl;
		delete[] message;

	}

	for (unsigned int i = 0; i < shaders.size(); i++) {
		glDetachShader(program, shaders[i]);
	}

	return program;
}

void drawObjectsInit() {

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);


	glGenBuffers(2, vertexBuffer);

	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions2), vertexPositions2, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	vector<GLuint> shaders;
	shaders.push_back(createShader(GL_VERTEX_SHADER, strVertexShader));
	shaders.push_back(createShader(GL_FRAGMENT_SHADER, strFragmentShader));

	renderProgram = createProgram(shaders);

	for_each(shaders.begin(), shaders.end(), glDeleteShader);

	shaders.push_back(createShader(GL_VERTEX_SHADER, strVertexShader));
	shaders.push_back(createShader(GL_FRAGMENT_SHADER, strFragmentShader2));

	renderProgram2 = createProgram(shaders);

	for_each(shaders.begin(), shaders.end(), glDeleteShader);


}

void draw() {

	glClear(GL_COLOR_BUFFER_BIT);

	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)48);


	glUseProgram(renderProgram);
	glDrawArrays(GL_TRIANGLES, 0, 3);

	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);

	glUseProgram(renderProgram2);
	glDrawArrays(GL_TRIANGLES, 0, 3);

	glfwSwapBuffers();

}

int main() {

	cout << getCurrentTime() << ": " << "Started" << endl;

	if (glfwInit() == GL_FALSE) {
		cout << getCurrentTime() << ": " << "GLFW initialization error" << endl;
		return 1;
	}

	glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); // 4x antialiasing
    glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3); // We want OpenGL 3.2
    glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
    glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //We don't want the old OpenGL
    glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);


	GLuint openResult = glfwOpenWindow(500, 500, 0, 0, 0, 0, 0, 0, GLFW_WINDOW);
	glfwSetWindowTitle( "nyaa" );

	if (openResult == GL_FALSE) {
		cout << getCurrentTime() << ": GLFW open window error";
		glfwTerminate();
		return 1;
	}

	glewExperimental=true; // Needed in core profile
	GLenum code = glewInit();
	if (code != GLEW_OK) {
		cout << getCurrentTime() << ": " << "GLEW initialization error" << endl;
		cout << getCurrentTime() << ": " << code << endl;
		return -1;
	}

	drawObjectsInit();

	int escPressed = GLFW_RELEASE;
	while (escPressed == GLFW_RELEASE) {

		escPressed = glfwGetKey(GLFW_KEY_ESC);

		draw();
	}

	glfwTerminate();

	cout << getCurrentTime() << ": " << "Finished" << endl;

	return 0;
}
